package de.ipk_gatersleben.bit.bi.edal.primary_data;

import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.UnknownHostException;

import org.eclipse.jetty.http.ssl.SslContextFactory;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import de.ipk_gatersleben.bit.bi.edal.primary_data.file.EdalException;

public class EdalJettyServer {

	public static final String EDAL_PATH_SEPARATOR = "/";

	private static URL url = null;

	private boolean useSSL = false;

	private Server eDALServer = null;

	protected EdalJettyServer(final EdalConfiguration configuration) {

		this.useSSL = configuration.isUseSSLForHttpListener();

		this.eDALServer = new Server();

		if (this.useSSL) {

			SslContextFactory contextFactory = new SslContextFactory();

			contextFactory.setKeyStore(configuration
					.getCertificatePathForHttpListener().getPath());
			contextFactory.setKeyStorePassword(configuration
					.getKeystorePasswordForHttpListener());

			SslSelectChannelConnector sslConnector = new SslSelectChannelConnector(
					contextFactory);
			sslConnector.setThreadPool(new QueuedThreadPool());

			try {
				sslConnector.setPort(configuration.getHttpPort());
				this.eDALServer.setConnectors(new Connector[] { sslConnector });
				this.eDALServer.setHandler(new EdalJettyHandler());

				this.setURL(new URL("https://"
						+ InetAddress.getLocalHost().getCanonicalHostName()
						+ ":" + configuration.getHttpPort()));
			} catch (MalformedURLException | UnknownHostException
					| EdalConfigurationException e) {
				e.printStackTrace();
			}

		} else {

			SelectChannelConnector nosslConnector = new SelectChannelConnector();

			try {
				nosslConnector.setPort(configuration.getHttpPort());
				nosslConnector.setThreadPool(new QueuedThreadPool());
				this.eDALServer.setConnectors(new Connector[] { nosslConnector });
				this.eDALServer.setHandler(new EdalJettyHandler());

				this.setURL(new URL("http://"
						+ InetAddress.getLocalHost().getCanonicalHostName()
						+ ":" + configuration.getHttpPort()));

			} catch (MalformedURLException | UnknownHostException
					| EdalConfigurationException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * Start the eDAL {@link org.eclipse.jetty.server.Server}.
	 */
	protected void start() {
		try {
			this.eDALServer.start();
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (this.useSSL) {
			DataManager
					.getImplProv()
					.getLogger()
					.info("HTTPS-Server is listening on: "
							+ this.eDALServer.getConnectors()[0].getHost());
		} else {
			DataManager
					.getImplProv()
					.getLogger()
					.info("HTTP-Server is listening on: "
							+ this.eDALServer.getConnectors()[0].getHost());
		}

	}

	/**
	 * Stop the eDAL {@link org.eclipse.jetty.server.Server}.
	 */
	protected void stop() {

		try {
			this.eDALServer.stop();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * internal setter for the {@link URL} of the HTTP server.
	 * 
	 * @param url
	 *            thwe {@link URL} to set.
	 */
	private void setURL(URL url) {
		EdalJettyServer.url = url;
	}

	/**
	 * Get the {@link URL} of the HTTP server.
	 * 
	 * @return the {@link URL} of the HTTP server.
	 * @throws EdalException
	 *             if no HTTP server was started.
	 */
	public static URL getServerURL() throws EdalException {
		if (EdalJettyServer.url != null) {
			return EdalJettyServer.url;
		} else {
			throw new EdalException("no eDAL HTTP server started");
		}
	}

	/**
	 * Generate an {@link URL} to a ticket for the given method.
	 * 
	 * @param ticket
	 *            the ticket to the method.
	 * @param reviewerCode
	 *            the code to identify the reviewer.
	 * @param method
	 *            the method for this {@link URL}.
	 * @return the URL to accept the
	 *         {@link de.ipk_gatersleben.bit.bi.edal.primary_data.file.PublicReference}
	 *         .
	 * @throws EdalException
	 *             if unable to generate an URL.
	 */
	public static URL generateMethodURL(String ticket, int reviewerCode,
			EdalHttpFunctions method) throws EdalException {
		try {
			URI uri = new URI(getServerURL().toString());

			URL methodURL = new URL(uri.getScheme() + "://" + uri.getHost()
					+ ":" + uri.getPort() + EdalJettyServer.EDAL_PATH_SEPARATOR
					+ method + EdalJettyServer.EDAL_PATH_SEPARATOR + ticket
					+ EdalJettyServer.EDAL_PATH_SEPARATOR + reviewerCode);

			return methodURL;
		} catch (URISyntaxException | MalformedURLException e) {
			throw new EdalException("unable to generate URL for " + method, e);
		}
	}

	/**
	 * Generate an {@link URL} to access a
	 * {@link de.ipk_gatersleben.bit.bi.edal.primary_data.file.PrimaryDataEntity}
	 * as reviewer over a temporal landing page.
	 * 
	 * @param entityURL
	 *            the normal {@link URL} to this
	 *            {@link de.ipk_gatersleben.bit.bi.edal.primary_data.file.PrimaryDataEntity}
	 *            .
	 * @param reviewersCode
	 *            the code to identify the reviewer
	 * @return the {@link URL} to access the landing page for the reviewer.
	 * @throws EdalException
	 */
	public static URL generateReviewerURL(URL entityURL, int reviewersCode)
			throws EdalException {

		try {
			URL reviewerUrl = new URL(entityURL.toString()
					+ EdalJettyServer.EDAL_PATH_SEPARATOR + reviewersCode);
			return reviewerUrl;
		} catch (MalformedURLException e) {
			throw new EdalException("unable to generate ReviewerURL", e);
		}
	}
}
